#include "connection.h"

#include <QInputDialog>
#include <xmpp_task.h>
#include <xmpp_tasks.h>
#include <pongserver.h>

#include "communityinterface.h"

extern CommunityInterface _c;

using namespace XMPP;

extern
bool Connection::isConnected()const
{
    return connected;
}

bool Connection::isActive()const
{
    return active;
}

Connection::Connection(QObject *parent, Optra::Proxy* p) :
        QObject(parent), active(false), connected(false), init_(0), px_(p)
{

}


Connection::~Connection()
{   

}

void Connection::setUser(const QString& user)
{
    jid_.setNode(user);
}

void Connection::setPassword(const QString& password)
{
    pass_ = password;
}


void Connection::sessionStarted()
{
    qDebug() << "Session Started";
    client_->rosterRequest();
}

void  Connection::cs_authenticated()
{

    //    if (conn_->isNull() || stream_->isNull()) {
    //        qDebug() << "AAAAAAAAAAAAA";
    //        return;
    //    }
    client_->start( jid_.domain(), jid_.node(), pass_, jid_.resource() );
    conn_->changePollInterval(10);

    qDebug() << "Authenticated on server - sweet!";

    if (!stream_->jid().isEmpty()) {
        jid_ = stream_->jid().bare();
    }

    QString resource = (stream_->jid().resource().isEmpty() ? px_->localHostName() : stream_->jid().resource());

    //client_->start(jid_.domain(), jid_.node(), pass_, resource);

    client_->start( jid_.domain(), jid_.node(), pass_, jid_.resource() );
    if (!stream_->old()) {
        JT_Session *j = new JT_Session(client_->rootTask());
        connect(j,SIGNAL(finished()),SLOT(sessionStart_finished()));
        j->go(true);
    }else{
        sessionStarted();
    }
}

void Connection::conn_srvLookup(const QString &server)
{
    qDebug() << (QString("SRV lookup on [%1]").arg(server));
}

void Connection::conn_srvResult(bool b)
{
    if(b)
        qDebug() << "SRV lookup success!";
    else
        qDebug() << "SRV lookup failed";
}

void Connection::conn_httpSyncStarted()
{
    qDebug() << ("HttpPoll: syncing");
}

void Connection::conn_httpSyncFinished()
{
    qDebug() << ("HttpPoll: done");
}

void Connection::tls_handshaken()
{
    //QCA::Certificate cert = tls->peerCertificate();
    int vr = tls->peerIdentityResult();
    if (vr == QCA::TLS::Valid && !tlsHandler->certMatchesHostname()) vr = QCA::TLS::HostMismatch;

    qDebug() << ("Successful TLS handshake.");
    if(vr == QCA::TLS::Valid)
        qDebug() << ("Valid certificate.");
    else {
        //qDebug() << (QString("Invalid certificate: %1").arg(resultToString(vr)), Qt::red);
        qDebug() << ("Continuing anyway");
    }

    tlsHandler->continueAfterHandshake();
}


void Connection::cs_connected()
{
    QString s = "Connected";
    if(conn_->havePeerAddress())
        s += QString(" (%1:%2)").arg(conn_->peerAddress().toString()).arg(conn_->peerPort());
    if(conn_->useSSL())
        s += " [ssl]";
    qDebug() << (s);
}

void Connection::cs_securityLayerActivated(int type)
{
    qDebug() << (QString("Security layer activated (%1)").arg((type == XMPP::ClientStream::LayerTLS) ? "TLS": "SASL"));
}

void  Connection::cs_needAuthParams( bool user, bool passwd, bool realm )
{

    if(user){
        qDebug() << "User Request";
        stream_->setUsername(jid_.node());        
    }

    if(passwd){
        qDebug() << "Pass Request";
        conn_->changePollInterval(10); // slow down during prompt
        bool ok;
        if (pass_.isEmpty()){
            QString s = QInputDialog::getText(NULL, tr("Password"), tr("Enter the password for %1").arg(jid_.full()), QLineEdit::Password, QString(), &ok);
            if(!ok) {
                stop();
                return;
            }
            pass_ = s;
        }
        stream_->setPassword(pass_);

        conn_->changePollInterval(2); // resume speed        
    }

    if (realm){
        qDebug() << "Realm Request";
        stream_->setRealm(jid_.domain());        
    }
    qDebug() << "Sending auth params ...";
    stream_->continueAfterParams();

}


void Connection::cs_connectionClosed()
{
    qDebug() << ("Disconnected by peer");
    cleanup();
}

void Connection::cs_delayedCloseFinished()
{
    qDebug() << ("cs_delayedCloseFinished");
    cleanup();
}

void Connection::cs_readyRead()
{
    while(stream_->stanzaAvailable()) {
        XMPP::Stanza s = stream_->read();
        qDebug() << (XMPP::Stream::xmlToString(s.element(), true));
    }
}

void Connection::cs_stanzaWritten()
{
    qDebug() << ("Stanza sent");
}


void Connection::start()
{

    init_ = new QCA::Initializer;
    if(active)
        return;    

    DiscoItem::Identity identity;
    identity.category = "client";
    identity.type = "pc";
    identity.name = "OptraCommunity";

    jid_ = "paskud@gromnica/optra";

    conn_ = new XMPP::AdvancedConnector;
    connect(conn_, SIGNAL(srvLookup(const QString &)), SLOT(conn_srvLookup(const QString &)));
    connect(conn_, SIGNAL(srvResult(bool)), SLOT(conn_srvResult(bool)));
    connect(conn_, SIGNAL(httpSyncStarted()), SLOT(conn_httpSyncStarted()));
    connect(conn_, SIGNAL(httpSyncFinished()), SLOT(conn_httpSyncFinished()));

    bool useHost = true;
    QString host = "127.0.0.1";
    int port = 5222;

    if(useHost)
        conn_->setOptHostPort(host, port);
    else
        conn_->setOptHostPort("", 0);

    if(QCA::isSupported("tls")) {
        tls = new QCA::TLS;
        tlsHandler = new XMPP::QCATLSHandler(tls);
        tlsHandler->setXMPPCertCheck(true);
        connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
    }
    else {
        tls = 0;
        tlsHandler = 0;
    }

    if(tls) {
        tls->setTrustedCertificates(QCA::systemStore());
    }

    stream_ = new XMPP::ClientStream(conn_, 0);

    connect(stream_, SIGNAL(connected()), SLOT(cs_connected()));
    connect(stream_, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated(int)));
    connect(stream_, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
    connect(stream_, SIGNAL(authenticated()), SLOT(cs_authenticated()));
    connect(stream_, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
    connect(stream_, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
    // connect(stream_, SIGNAL(readyRead()), SLOT(cs_readyRead()));
    // connect(stream_, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
    connect(stream_, SIGNAL(warning(int)), SLOT(cs_warning(int)));
    connect(stream_, SIGNAL(error(int)), SLOT(cs_error(int)));
    //

    stream_->setNoopTime(55000);
    stream_->setCompress(true);


    client_ = new XMPP::Client(stream_);

    client_->setIdentity(identity);

    QStringList features;
    features << "http://jabber.org/protocol/commands";
    features << "http://jabber.org/protocol/rosterx";
    features << "http://jabber.org/protocol/muc";
    features << "jabber:x:data";
    client_->setFeatures(Features(features));
    client_->setFileTransferEnabled(true);


    connect(client_, SIGNAL(xmlIncoming(const QString &)), SLOT(client_xmlIncoming(const QString &)));
    connect(client_, SIGNAL(xmlOutgoing(const QString &)), SLOT(client_xmlOutgoing(const QString &)));

    connect(client_, SIGNAL(rosterRequestFinished(bool, int, const QString &)), SLOT(client_rosterRequestFinished(bool, int, const QString &)));
    connect(client_, SIGNAL(rosterItemAdded(const RosterItem &)), SLOT(client_rosterItemAdded(const RosterItem &)));
    connect(client_, SIGNAL(rosterItemUpdated(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &)));
    connect(client_, SIGNAL(rosterItemRemoved(const RosterItem &)), SLOT(client_rosterItemRemoved(const RosterItem &)));

    connect(client_, SIGNAL(subscription(const Jid &, const QString &, const QString&)), SLOT(client_subscription(const Jid &, const QString &, const QString&)));

    //connect(client_, SIGNAL(debugText(const QString &)), SLOT(client_debugText(const QString &)));

    new PongServer(client_->rootTask());

    client_->addExtension("html", Features("http://jabber.org/protocol/xhtml-im"));

    connected = false;
    active = true;

    Jid j = jid_.withResource(px_->localHostName());
    client_->connectToServer(stream_, j);
}

void Connection::stop()
{
    cleanup();
    stream_->close();
    delete client_;
    delete stream_;
    delete tls;
    tlsHandler = 0;
    delete conn_;
    conn_ = 0;
    QCA::unloadAllPlugins();
    delete init_;    
}

void Connection::cleanup()
{
    active = false;
    connected = false;
}



void Connection::client_rosterItemAdded(const RosterItem &r)
{
    _c.info(QString("roster rtem added:%1").arg(r.jid().full()));
}

void Connection::client_rosterItemUpdated(const RosterItem &r)
{
    _c.info(QString("roster rtem updated:%1").arg(r.jid().full()));
}

void Connection::client_rosterItemRemoved(const RosterItem &r)
{
    _c.info(QString("roster rtem removed:%1").arg(r.jid().full()));
}

void Connection::client_rosterRequestFinished(bool success, int c, const QString & str)
{        
    if (success){
        connected = true;
        _c.info(QString("roster request finished success:%1").arg(success));
        emit ready();
        return;
    }
    _c.warning(QString("roster request failed str:%1").arg(str));
}

void Connection::client_subscription(const Jid &j, const QString &str, const QString& nick)
{
    // if they remove our subscription, then we lost presence
    if(str == "unsubscribed") {

    }
}

void Connection::sessionStart_finished()
{
    JT_Session *j = (JT_Session*)sender();
    if (j->success()) {
        sessionStarted();
    }
    else {
        _c.critical("session error");
    }
}

void Connection::client_debugText(const QString &str)
{
    _c.trace(str);
}

void  Connection::cs_error( int err )
{
    if(err == XMPP::ClientStream::ErrParse) {
        _c.critical("XML parsing error");
    }
    else if(err == XMPP::ClientStream::ErrProtocol) {
         _c.critical("XMPP protocol error");
    }
    else if(err == XMPP::ClientStream::ErrStream) {
        int x = stream_->errorCondition();
        QString s;
        if(x == XMPP::Stream::GenericStreamError)
            s = "generic stream error";
        else if(x == XMPP::ClientStream::Conflict)
            s = "conflict (remote login replacing this one)";
        else if(x == XMPP::ClientStream::ConnectionTimeout)
            s = "timed out from inactivity";
        else if(x == XMPP::ClientStream::InternalServerError)
            s = "internal server error";
        else if(x == XMPP::ClientStream::InvalidFrom)
            s = "invalid from address";
        else if(x == XMPP::ClientStream::InvalidXml)
            s = "invalid XML";
        else if(x == XMPP::ClientStream::PolicyViolation)
            s = "policy violation.  go to jail!";
        else if(x == XMPP::ClientStream::ResourceConstraint)
            s = "server out of resources";
        else if(x == XMPP::ClientStream::SystemShutdown)
            s = "system is shutting down NOW";
        _c.critical(QString("XMPP stream error: %1").arg(s));
    }
    else if(err == XMPP::ClientStream::ErrConnection) {
        int x = conn_->errorCode();
        QString s;
        if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
            s = "unable to connect to server";
        else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
            s = "host not found";
        else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
            s = "proxy connect";
        else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
            s = "proxy negotiating";
        else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
            s = "proxy authorization";
        else if(x == XMPP::AdvancedConnector::ErrStream)
            s = "stream error";
         _c.critical(QString("Connection error: %1").arg(s));
    }
    else if(err == XMPP::ClientStream::ErrNeg) {
        int x = stream_->errorCondition();
        QString s;
        if(x == XMPP::ClientStream::HostGone)
            s = "host no longer hosted";
        else if(x == XMPP::ClientStream::HostUnknown)
            s = "host unknown";
        else if(x == XMPP::ClientStream::RemoteConnectionFailed)
            s = "a required remote connection failed";
        else if(x == XMPP::ClientStream::SeeOtherHost)
            s = QString("see other host: [%1]").arg(stream_->errorText());
        else if(x == XMPP::ClientStream::UnsupportedVersion)
            s = "server does not support proper xmpp version";
        _c.critical(QString("Stream negotiation error: %1").arg(s));
    }
    else if(err == XMPP::ClientStream::ErrTLS) {
        int x = stream_->errorCondition();
        QString s;
        if(x == XMPP::ClientStream::TLSStart)
            s = "server rejected STARTTLS";
        else if(x == XMPP::ClientStream::TLSFail) {
            int t = tlsHandler->tlsError();
            if(t == QCA::TLS::ErrorHandshake)
                s = "TLS handshake error";
            else
                s = "broken security layer (TLS)";
        }
        _c.critical(s);
    }
    else if(err == XMPP::ClientStream::ErrAuth) {
        int x = stream_->errorCondition();
        QString s;
        if(x == XMPP::ClientStream::GenericAuthError)
            s = "unable to login";
        else if(x == XMPP::ClientStream::NoMech)
            s = "no appropriate auth mechanism available for given security settings";
        else if(x == XMPP::ClientStream::BadProto)
            s = "bad server response";
        else if(x == XMPP::ClientStream::BadServ)
            s = "server failed mutual authentication";
        else if(x == XMPP::ClientStream::EncryptionRequired)
            s = "encryption required for chosen SASL mechanism";
        else if(x == XMPP::ClientStream::InvalidAuthzid)
            s = "invalid authzid";
        else if(x == XMPP::ClientStream::InvalidMech)
            s = "invalid SASL mechanism";
        else if(x == XMPP::ClientStream::InvalidRealm)
            s = "invalid realm";
        else if(x == XMPP::ClientStream::MechTooWeak)
            s = "SASL mechanism too weak for authzid";
        else if(x == XMPP::ClientStream::NotAuthorized)
            s = "not authorized";
        else if(x == XMPP::ClientStream::TemporaryAuthFailure)
            s = "temporary auth failure";
        _c.critical(QString("Auth error: %1").arg(s));
    }
    else if(err == XMPP::ClientStream::ErrSecurityLayer)
        _c.critical("Broken security layer (SASL)");

    cleanup();
}
void  Connection::cs_warning( int warn )
{
    if(warn == XMPP::ClientStream::WarnOldVersion) {
         _c.warning("Warning: pre-1.0 protocol server");
    }
    else if(warn == XMPP::ClientStream::WarnNoTLS) {
        _c.warning("Warning: TLS not available!");
    }
    stream_->continueAfterWarning();
}

void Connection::client_xmlIncoming(const QString &s)
{
    qDebug() << "--------XML IN------";
    _c.trace(s);
    qDebug() << "---------------------";
}

void Connection::client_xmlOutgoing(const QString &s)
{
    qDebug() << "--------XML OUT------";
    _c.trace(s);
    qDebug() << "---------------------";
}
